1   /*
2    * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  
24  /*
25   * Portions Copyright (c) 2011 IBM Corporation
26   */
27  
28  /*
29   * @test
30   * @bug 7031076
31   * @summary Allow stale InputStreams from ZipFiles to be GC'd
32   * @author Neil Richards <neil.richards@ngmr.net>, <neil_richards@uk.ibm.com>
33   */
34  import java.lang.ref.ReferenceQueue;
35  import java.lang.ref.WeakReference;
36  import java.io.File;
37  import java.io.FileOutputStream;
38  import java.io.InputStream;
39  import java.util.Enumeration;
40  import java.util.HashSet;
41  import java.util.Random;
42  import java.util.Set;
43  import java.util.zip.ZipEntry;
44  import java.util.zip.ZipFile;
45  import java.util.zip.ZipOutputStream;
46  
47  public class ClearStaleZipFileInputStreams {
48      private static final int ZIP_ENTRY_NUM = 5;
49  
50      private static final byte[][] data;
51  
52      static {
53          data = new byte[ZIP_ENTRY_NUM][];
54          Random r = new Random();
55          for (int i = 0; i < ZIP_ENTRY_NUM; i++) {
56              data[i] = new byte[1000];
57              r.nextBytes(data[i]);
58          }
59      }
60  
61      private static File createTestFile(int compression) throws Exception {
62          File tempZipFile =
63              File.createTempFile("test-data" + compression, ".zip");
64          tempZipFile.deleteOnExit();
65  
66          ZipOutputStream zos =
67              new ZipOutputStream(new FileOutputStream(tempZipFile));
68          zos.setLevel(compression);
69  
70          try {
71              for (int i = 0; i < ZIP_ENTRY_NUM; i++) {
72                  String text = "Entry" + i;
73                  ZipEntry entry = new ZipEntry(text);
74                  zos.putNextEntry(entry);
75                  try {
76                      zos.write(data[i], 0, data[i].length);
77                  } finally {
78                      zos.closeEntry();
79                  }
80              }
81          } finally {
82              zos.close();
83          }
84  
85          return tempZipFile;
86      }
87  
88      private static void startGcInducingThread(final int sleepMillis) {
89          final Thread gcInducingThread = new Thread() {
90              public void run() {
91                  while (true) {
92                      System.gc();
93                      try {
94                          Thread.sleep(sleepMillis);
95                      } catch (InterruptedException e) { }
96                  }
97              }
98          };
99  
100         gcInducingThread.setDaemon(true);
101         gcInducingThread.start();
102     }
103 
104     public static void main(String[] args) throws Exception {
105         startGcInducingThread(500);
106         runTest(ZipOutputStream.DEFLATED);
107         runTest(ZipOutputStream.STORED);
108     }
109 
110     private static void runTest(int compression) throws Exception {
111         ReferenceQueue<InputStream> rq = new ReferenceQueue<>();
112 
113         System.out.println("Testing with a zip file with compression level = "
114                 + compression);
115         File f = createTestFile(compression);
116         try {
117             ZipFile zf = new ZipFile(f);
118             try {
119                 Set<Object> refSet = createTransientInputStreams(zf, rq);
120 
121                 System.out.println("Waiting for 'stale' input streams from ZipFile to be GC'd ...");
122                 System.out.println("(The test will hang on failure)");
123                 while (false == refSet.isEmpty()) {
124                     refSet.remove(rq.remove());
125                 }
126                 System.out.println("Test PASSED.");
127                 System.out.println();
128             } finally {
129                 zf.close();
130             }
131         } finally {
132             f.delete();
133         }
134     }
135 
136     private static Set<Object> createTransientInputStreams(ZipFile zf,
137             ReferenceQueue<InputStream> rq) throws Exception {
138         Enumeration<? extends ZipEntry> zfe = zf.entries();
139         Set<Object> refSet = new HashSet<>();
140 
141         while (zfe.hasMoreElements()) {
142             InputStream is = zf.getInputStream(zfe.nextElement());
143             refSet.add(new WeakReference<InputStream>(is, rq));
144         }
145 
146         return refSet;
147     }
148 }